home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 3 / Amiga Tools 3.iso / grafik / raytracing / rayshade-4.0.6.3 / inetray / poo < prev    next >
Text File  |  1993-08-15  |  17KB  |  329 lines

  1. ======================================================================
  2.                     P O O 
  3.                     doc: Thu Apr  2 11:59:51 1992
  4.                     dlm: Wed Jul 21 14:37:58 1993
  5.                     (c) 1992 ant@ips.id.ethz.ch
  6.                     uE-Info: 266 0 NIL 0 0 72 3 2 8 ofnI
  7. ======================================================================
  8.  
  9. This file describes some interna of the inetray-packet. It's name
  10. derives from Principles of Operation.
  11.  
  12. Overview
  13. --------
  14. The program inetray is responsible for dispatching and scheduling the
  15. rayshade requests. In the usual terminology it acts as the client
  16. requesting services from a number of remotely running servers. It does
  17. that using SUN RPC. Rendering requests do not block, therefore inetray also
  18. listens continuously on a socket to check for incoming results. The data
  19. which is received from the workers is written to the file whenever this
  20. is possible.
  21.  
  22. The program rpc.inetrayd serves two purposes: it services a number of
  23. rpc-requests dealing with initialization and management. Whenever it
  24. receives a rendering request, it spawns of a worker child and continues
  25. to service rpc requests (a restricted number).
  26. The worker now renders a part of a frame and then directly contacts the
  27. dispatcher to send it the result. This is done using a XDR/TCP
  28. connection.
  29.  
  30. inetray.start is a simple RPC daemon servicing requests for starting the
  31. rpc.inetrayd servers.
  32.  
  33. Rayshade Libraries
  34. ------------------
  35. Inetray uses the standard Rayshade libraries (as used in version 4.0.6).
  36. Great care has been taken to avoid having to change the libraries at
  37. all. As long as the interface stays the same no change is required for
  38. Inetray even if rayshade evolves.
  39. At times this decision not to change the rayshade source lead to
  40. complicated and maybe clumsy solutions. But in the interest of
  41. portability it has nevertheless been adhered to strictly.
  42. There is one currently unsolved problem arising from this decision: it
  43. seems that the random generator used for textures is different on big
  44. and little-endian machines. Therefore they don't mix. A solution has
  45. been promised for rayshade 5 by Craig Kolb but so far it isn't clear
  46. when it'll be out.
  47.  
  48. Input
  49. -----
  50. rayshade accepts its input in various ways:
  51.  
  52. 1) rayshade "filename"        (input is file)
  53. 2) ... | rayshade        (input is stdin/pipe)
  54. 3) rayshade < "filename"    (input is stdin/file)
  55. 4) rayshade             (input is stdin/keyboard)
  56.  
  57. Inetray from version 1.1.0 on provides total compatibility with all
  58. those possibilities. It does this by buffering stdin in a live buffer
  59. (see below). RSInitialize() is then called taking its stdin from this
  60. buffer. Note that in case 1 the buffer is not needed and should indeed
  61. disappear because it competes with inetray for the keyboard. This
  62. problem is solved in a very simple manner: on buffer startup SIGINT is
  63. set to kill the buffer; once the buffer encounters an eof (i.e. cases 2
  64. 3 & 4, where eof is necessarily encountered before RSInitialize()
  65. returns) SIGINT is ignored. Inetray (the parent) sends a SIGINT to the
  66. buffer on return from RSInitialize. If the buffer still has the stdin
  67. open (necessarily the keyboard) it is killed, otherwise it continues
  68. running.
  69. Case 1 was the only one allowed for Inetray up to version 1.0.1. It
  70. requires the input file to exist on all worker-machines. To increase
  71. the flexibility, the inetray workers try to have the ``same''
  72. working-directory as the dispatcher (see section Pathnames below).
  73. Note that even when stdin is used for input, the input can contain
  74. references to other files to be read in, namely cpp #includes and
  75. height-fields. Those files must be accessed much in the same way as the
  76. files in case 1 (see above and section Pathnames). If no such files
  77. must be included then no file has to be accessible on the worker
  78. machines.
  79.  
  80. Case 4 is handled much like cases 2 & 3.
  81.  
  82. Live Buffers
  83. ------------
  84. A live buffer is just a forked process which first reads from one
  85. filedesc into memory (malloc'ed) and then writes the contents of the
  86. buffer to another (in some cases two) filedesc before terminating.
  87. End of input is detected when either an eof is reached or a \0 is read
  88. as the last character of a read() syscall. This feature allows to use
  89. live buffers to read from TCP connections which should not be closed.
  90. Live buffers are more expensive than writing a temporary file for large
  91. amounts of data and worsen the already problematic memory situation but
  92. they avoid having servers writing files which could be a possible
  93. security problem (see below).
  94. Note that live buffers terminate automatically once their respective
  95. parents disappeared. This is due to the fact that eventually they will
  96. encounter an eof on the input filedesc and start writing. They always
  97. write to a pipe so when the last reader of the pipe died they die on a
  98. SIGPIPE. 
  99.  
  100. Authentication & Security
  101. -------------------------
  102. If the servers (rpc.inetrayd) are started as root, they try to change to
  103. the user id supplied to them. This is usually the user id of the user
  104. running the dispatcher (inetray). Any user, however can set a different
  105. user id for servers started by inetray.start. No server can run as root
  106. (uid == 0).
  107. If the uid is illegal on the server it exits with an error message in
  108. the syslog.
  109. No server ever produces an output file. This therefore limits the
  110. security concerns to changing the access time of files. Of course it is
  111. possible that there are loopholes in this concept; I just haven't found
  112. one yet.
  113. If the server is not started as root, it will continue to run under the
  114. uid it was started as. One has to check the permissions of the accessed
  115. files for reading access for that user.
  116. The actual usernames under which the servers are running is diplayed by
  117. both inetray and inetray.ping.
  118.  
  119. Session Keys
  120. ------------
  121. Whenever a started server receives the first request, a session key is
  122. sent with that. Once a sessions key is installed, only requests with the
  123. same key are serviced. In practice this means that only the person who
  124. issued a inetray call can kill the running servers and workers. The key
  125. is stored in the file .inetray.key in the current directory where
  126. inetray was issued. An eventually existing file is renamed to
  127. .inetray.key.old.
  128. inetray displays the current session key on startup.
  129. inetray.ping uses the special key 0. Therefore, if servers hang after a
  130. inetray.ping, they can be killed with inetray.kill 0.
  131. The program inetray.kill needs a session key supplied with. If one is
  132. given as an argument, this takes precedence. If no key is supplied,
  133. inetray.kill looks for one in the file .inetray.key.
  134.  
  135. Version Numbers
  136. ---------------
  137. Both inetray and rpc.inetrayd know about their version number. The
  138. server passes this back to inetray and inetray.ping upon reception of
  139. the first request. Only if the first character (i.e. mayor version
  140. number) of this version number  matches, the worker is accepted.
  141.  
  142. Pathnames
  143. ---------
  144. Since servers can be running on machines with totally different
  145. filesystems but may need to access the inputfiles locally, some
  146. pathname substitution is supported.
  147. All filenames are transferred as-is to the servers/workers. If they
  148. start with a / they are absolute path-names starting at the respective
  149. root of the machines. This will probably not work well on all but the
  150. most homogenous networks.
  151. If they don't start with a / they are relative names starting in the
  152. current working directory.  From the working directory where the client
  153. is started the home-part is stripped if possible. This stripped
  154. directory is then sent to the server which in turn adds the home
  155. directory of the uid it is to run as.
  156. Note that if nothing was stripped on the client-side, then nothing is
  157. added on the server-side. Note also that the right directory is chosen
  158. even when the server cannot run under that user id.
  159. The server tries to chdir to the directory so constructed. If that
  160. fails it continues to run in the current directory which is the
  161. directory where it was started from. The working directories of the
  162. servers are displayed by both inetray and inetray.ping.
  163. The practical abshot is that if you have the same sub-directory
  164. structure below your home on the different machines, you can start
  165. Inetray in all these directories and the servers/workers will cwd() to
  166. the right sub-dirs as well.
  167.  
  168. Port Numbers
  169. ------------
  170. The rendered portions of a frame are sent back using a XDR/TCP
  171. connection. The portnumber for this is defined in config.h (RESULTPORT)
  172. but can be overridden for each user in the .inetrayrc file.
  173.  
  174. Registering Servers
  175. -------------------
  176. Whenever inetray or inetray.ping are started, they try to register ready
  177. servers.
  178. First, the servers started by inetray.start are started; the servers
  179. started by inetd are started automatically when an INIT-request arrives. 
  180. The order in which the machines are contacted is the following:
  181.     1: All simple hosts given in the Use List (if any)
  182.     2: All directed broadcasts addresses in the Use List (if any)
  183.     3: The Local Network (if option N=0 is not set in the Use List)
  184. After starting, an INIT-request is sent to all machines. Servers that are
  185. to be started by inetd, are started automatically when they receive an
  186. INIT-request. The same order applies.
  187. Servers reply by opening a TCP-connection on the result-port and sending
  188. back status info.
  189. Answers may be ignored for two reasons: either the hostname appears
  190. (exactly as given) in the ignore list in the current .inetrayrc or the
  191. mayor version number of the server does not match that of the
  192. dispatcher.
  193. If the input comes from stdin, then the contents of the live buffer (see
  194. above) of the dispatcher is sent to live buffers on the server machines
  195. using the same TCP-connection. This is, however, only done once
  196. registering is otherwise completed (i.e. the list of registered machines
  197. is complete).
  198.  
  199. Work Scheduling
  200. ---------------
  201. A frame is divided into blocks encompassing > 1 lines. This is done
  202. according to a simple heuristics the parameters of which can be
  203. controlled by editing config.h and/or overriding those values in a
  204. .inetrayrc file (see INSTALL/Appendix B for details).
  205. After n workers have been registered, the block size is calculated as
  206. follows: blockSize = ySize / blocksPerServer / n. After that, the size
  207. is checked against the lower and upper limit (MINBLOCKSIZE resp.
  208. MAXBLOCKSIZE). If it exceeds a limit, it is adjusted accordingly. After
  209. that, the size of the last, possibly incomplete, block is calculated and
  210. the information printed.
  211.  
  212. In early versions (up to [0.2.0]), a simple round robin scheduling has
  213. been used: subseqent machines got subsequent blocks to trace; whenever
  214. the end of a frame was reached, the whole process started over with only
  215. the non-terminated blocks.
  216. This could lead to quite bad behaviour in the end. Consider for example
  217. the example file mole.ray. Early blocks (bottom half) take much longer
  218. to trace than later ones. If now one machine is heavily loaded, it won't
  219. ever complete its block. This means that there will one early block be
  220. outstanding for a very long time wich will inhibit concurrent writing.
  221. Furthermore, with a little bit of bad luck, this block will be the last
  222. one outstanding which will mean that a lot of machines will calculate
  223. just one block in the end. This block will take a long time to
  224. calculate.
  225. Starting with version [0.2.1] there is a rescheduling inserted in the
  226. middle of a frame. The number of machines which did not yet return a
  227. result is counted and the first n blocks (n being the number of those
  228. machines) not yet calculated are given priority over other blocks. These
  229. blocks are exactly those residing on those slow machines. Hopefully,
  230. these are distributed to faster machines like this.
  231. I my setting, this modification lead to quite a decrease in time needed
  232. to complete the last block.
  233. Notes: - The scheme presented here also works nicely if workers crash
  234.      during the first half of a frame (which they seem to tend to
  235.      do).
  236.  
  237. For version 2.0.0 the scheduling has changed yet again. For images where
  238. all the hard work is done in a small part of the picture the old
  239. scheduler didn't work very nicely. To solve this problem the following
  240. scheduler has been implemented:
  241.     - During the first round of work scheduling (i.e. until all
  242.       blocks have been dispatched once) the blocks are always
  243.       scheduled in pairs (i.e. one woker renders 2 blocks on every
  244.       request).
  245.     - When this 1st pass has been finished, only single blocks are
  246.       dispatched.
  247. It's not clear if this scheduler is always better than the earlier
  248. versions.
  249.  
  250. Concurrent Servers & RPC Program Numbers
  251. ----------------------------------------
  252. It is possible for one machine to have more than one server (and worker)
  253. running at a time. This feature is implemented to allow multiprocessor
  254. machines to have as many workers as processors running. A machine
  255. starting more than one worker cannot start it using inetd. Concurrent
  256. servers have different RPC Program Numbers. The first server gets the
  257. program number IRNUM defined in prognum.h. Subsequent servers get
  258. subsequent program numbers.
  259. Like that, registering with the portmapper works correctly. It must be
  260. noted, though, that all broadcasts to servers now must be broadcast for
  261. all program numbers.
  262.  
  263. Error Logging
  264. -------------
  265. The general mechanism is described in README and SUPPORT.
  266. Please note that also all errors produced by the rayshade routines are
  267. logged. This is done using a funny redirection of the stderr to the
  268. syslog using socketpairs and async I/O. For this to work under AUX I had
  269. to implement the socketpair() syscall there, since the one built in does
  270. not work (at least in our version).
  271.  
  272. Error Termination
  273. -----------------
  274. Roughly once every minute, every server checks if the dispatcher is
  275. still running. If that's not the case, it kills it's associated worker
  276. if it has one and then exits with an entry in the syslog.
  277. As from version 2.0.0 the server also checks the exit status of its
  278. child once every minute. If the child exited with a status != 0 it shuts
  279. itself down. This non-zero exit status can be due to two different
  280. reasons: either the rayshade libraries exited explicitly or the worker
  281. was terminated with a signal (either implicitly (bus error, segmentation
  282. violation, ...) or explicitly (it annoyed either your sysadm or
  283. yourself)). 
  284.  
  285. Socket State
  286. ------------
  287. Seems to me there's no clean way to extract the correct state of a
  288. socket without reading kernel memory. Nevertheless, the connection state
  289. must be retrieved for checking the state of the dispatcher. In a first
  290. test getpeername() was used. Unfortunately it returns the peername of
  291. the dispatcher even if that one has been killed (and the socked is in
  292. CLOSE_WAIT/FIN_WAIT_2 state).
  293. Up to version 1.0.1 select()'ing the socket for reading did the trick
  294. since it was used only as a one-way server->dispatcher connection. Thus
  295. being ready for reading meant an error.
  296. Later versions use the TCP connection to send the stdin (see Live
  297. Buffers above). Therefore checking the state of the connection means
  298. selecting it for read and testing it being empty at the same time.
  299. There's no UNIX syscall to do this. If it can be guaranteed that nobody
  300. reads the socket between selecting it for read and testing it for
  301. emptyness then we succeeded. Unfortunately there is a Live Buffer which
  302. reads the data written to the socket; this buffer is a separate process
  303. which does not sync itself with the server.
  304. The buffer can, however, not block forever in its reading state. It will
  305. stop reading if the buffer on the dispatcher side is exhausted or
  306. killed. After that it will start writing on the pipe. Therefore we can
  307. disallow checking the dispatcher for life while the server buffer is in
  308. reading state. It enters reading state immetiately when lPostBuffer() is
  309. called. By selecting the pipe for reading we can find out when the
  310. buffer is its writing state.
  311. Note that the socket is never written to (by the dispatcher) unless an
  312. INIT request has been successfully completed by the server. Therefore we
  313. don't even have to check for emptyness of the socket - selecting it for
  314. read whenever we can guarantee that the buffer is not reading it tells
  315. us therefore if the dispatcher is still running.
  316. If the buffer on the server side is killed before completing its reading
  317. then the server also terminates assuming the death of the dispatcher.
  318. This is ok. If the buffer dies during its writing period, the pipe is
  319. closed and returns eof for the reader which results in an error and exit
  320. there.
  321.  
  322. Rpc.inetrayd startup
  323. --------------------
  324. The server can be started up by inetd or inetray.start (or, for
  325. debugging purposes, by hand). It checks its number of arguments to
  326. decide how it was started up. If it is called without any arguments, it
  327. assumes that it is started by inetd. Therefore you have to supply a
  328. dummy argument if you want to start it by hand.
  329.